Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / domain / src / commonMain / kotlin / org / meshtastic / core / domain / usecase / session / EnsureRemoteAdminSessionUseCase.kt

Displaying Raw • Download

core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/session/EnsureRemoteAdminSessionUseCase.kt 2d20cd8a4708e2ef66e98d5259f3a97ec93f240a (2d20cd8a) Text, 4.88 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.domain.usecase.session

Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.coroutines.CoroutineScope
Tff7b72import T7ee787kotlinx.coroutines.CoroutineStart
Tff7b72import T7ee787kotlinx.coroutines.Deferred
Tff7b72import T7ee787kotlinx.coroutines.async
Tff7b72import T7ee787kotlinx.coroutines.flow.filter
Tff7b72import T7ee787kotlinx.coroutines.flow.first
Tff7b72import T7ee787kotlinx.coroutines.sync.Mutex
Tff7b72import T7ee787kotlinx.coroutines.sync.withLock
Tff7b72import T7ee787kotlinx.coroutines.withTimeoutOrNull
Tff7b72import T7ee787org.koin.core.annotation.Named
Tff7b72import T7ee787org.koin.core.annotation.Single
Tff7b72import T7ee787org.meshtastic.core.model.ConnectionState
Tff7b72import T7ee787org.meshtastic.core.model.SessionStatus
Tff7b72import T7ee787org.meshtastic.core.repository.RadioController
Tff7b72import T7ee787org.meshtastic.core.repository.ServiceRepository
Tff7b72import T7ee787org.meshtastic.core.repository.SessionManager
Tff7b72import T7ee787kotlin.time.Duration.Companion.seconds

T8b949e/**
* Ensures a remote-admin session exists for the target node, dispatching a metadata request and awaiting a refreshed
* passkey if necessary.
*
* Why this exists: the firmware embeds an 8-byte rotating passkey in every admin response and rejects admin traffic
* lacking a fresh key (`firmware/src/modules/AdminModule.cpp:1460-1481`). Before this use case the UI silently tunneled
* the user into a remote-admin screen that immediately failed if no metadata had been requested first.
*
* Concurrency model:
* - One in-flight ensure per `destNum`. Concurrent callers dedupe onto the same `Deferred` so a double-tap doesn't
* blast two metadata requests at the radio.
* - The refresh-flow subscription is established **before** the metadata request is dispatched to avoid losing the
* response on the inherently raceful `MutableSharedFlow`.
* - The `withTimeoutOrNull` is a UX deadline only — late responses still update the durable `SessionStatus` flow that
* the UI observes, so a "Timeout" outcome here can self-heal in the chip without re-tapping.
*/
Tf0883e@Single
Tff7b72open Tff7b72class T56d364EnsureRemoteAdminSessionUseCaseTb4b4b4(
Tff7b72private Tff7b72val Te6edf3sessionManagerTb4b4b4: Te6edf3SessionManagerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3radioControllerTb4b4b4: Te6edf3RadioControllerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3serviceRepositoryTb4b4b4: Te6edf3ServiceRepositoryTb4b4b4,
Tf0883e@NamedTb4b4b4(Ta5d6ff"Ta5d6ffServiceScopeTa5d6ff"Tb4b4b4) Tff7b72private Tff7b72val Te6edf3serviceScopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3mutex Tff7b72= Te6edf3MutexTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3inFlight Tff7b72= Te6edf3mutableMapOfTff7b72<Tffa657IntTb4b4b4, Te6edf3DeferredTff7b72<Te6edf3EnsureSessionResultTff7b72>Tff7b72>Tb4b4b4(Tb4b4b4)

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tff7b72open Tff7b72suspend Tff7b72operator Tff7b72fun Td2a8ffinvokeTb4b4b4(Te6edf3destNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3EnsureSessionResult Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3serviceRepositoryTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3value Tff7b72!Tff7b72= Te6edf3ConnectionStateTb4b4b4.Te6edf3ConnectedTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3EnsureSessionResultTb4b4b4.Te6edf3Disconnected
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3sessionManagerTb4b4b4.Te6edf3observeSessionStatusTb4b4b4(Te6edf3destNumTb4b4b4)Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4) Tff7b72is Te6edf3SessionStatusTb4b4b4.Te6edf3ActiveTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3EnsureSessionResultTb4b4b4.Te6edf3AlreadyActive
Tb4b4b4}

Tff7b72val Te6edf3deferred Tff7b72=
Te6edf3mutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Te6edf3inFlightTff7b72[Te6edf3destNumTff7b72]
Tff7b72?: Te6edf3serviceScope
Tb4b4b4.Te6edf3asyncTb4b4b4(Te6edf3start Tff7b72= Te6edf3CoroutineStartTb4b4b4.Te6edf3LAZYTb4b4b4) Tb4b4b4{ Te6edf3runEnsureTb4b4b4(Te6edf3destNumTb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3also Tb4b4b4{ Te6edf3inFlightTff7b72[Te6edf3destNumTff7b72] Tff7b72= Tffa657it Tb4b4b4}
Tb4b4b4}
Tff7b72return Tff7b72try Tb4b4b4{
Te6edf3deferredTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3mutexTb4b4b4.Te6edf3withLock Tb4b4b4{ Tff7b72if Tb4b4b4(Te6edf3inFlightTff7b72[Te6edf3destNumTff7b72] Tff7b72=Tff7b72=Tff7b72= Te6edf3deferredTb4b4b4) Te6edf3inFlightTb4b4b4.Te6edf3removeTb4b4b4(Te6edf3destNumTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffrunEnsureTb4b4b4(Te6edf3destNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3EnsureSessionResult Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffEnsureRemoteAdminSession dispatching metadata request to Tffd700$Te6edf3destNumTa5d6ff" Tb4b4b4}
Tff7b72return Te6edf3withTimeoutOrNullTb4b4b4(Te6edf3UX_TIMEOUTTb4b4b4) Tb4b4b4{
T8b949e// Subscribe BEFORE dispatching so we don't miss the refresh emission.
Tff7b72val Te6edf3refreshed Tff7b72=
Te6edf3serviceScopeTb4b4b4.Te6edf3asyncTb4b4b4(Te6edf3start Tff7b72= Te6edf3CoroutineStartTb4b4b4.Te6edf3UNDISPATCHEDTb4b4b4) Tb4b4b4{
Te6edf3sessionManagerTb4b4b4.Te6edf3sessionRefreshFlowTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3destNum Tb4b4b4}Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72try Tb4b4b4{
Te6edf3radioControllerTb4b4b4.Te6edf3refreshMetadataTb4b4b4(Te6edf3destNumTb4b4b4)
Te6edf3refreshedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Te6edf3EnsureSessionResultTb4b4b4.Te6edf3Refreshed
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3refreshedTb4b4b4.Te6edf3cancelTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72?: Te6edf3EnsureSessionResultTb4b4b4.Te6edf3Timeout
Tb4b4b4}

Tff7b72companion Tff7b72object Tb4b4b4{
T8b949e/**
* UX deadline for surfacing a result to the user. The metadata request keeps flying after this — late responses
* still update the durable `SessionStatus` flow.
*/
Tff7b72val Te6edf3UX_TIMEOUT Tff7b72= T79c0ff1T79c0ff0.Te6edf3seconds
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.05s